package com.example.myapp;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;

/**
 * 可伸缩的ListView,设置最大值和最小值，LsitView初始显示最小值内容，上滑后先展开，到达最大值位置，之后item滑动展现全部内容。下滑反之。
 * 此ListView如果和ScrollView配合使用将会出现问题。
 */
public class RetractableListView extends ListView {

    /**
     * listview整体移动模式
     */
    private static final int LISTVIEW_MOVE = 1;
    /**
     * listview中item移动模式
     */
    private static final int ITEM_MOVE = 2;

    /**
     * 初始移动位置
     */
    private int mDefaultTranslationY;

    /**
     * 父布局高度
     */
    private int mParentHeight;

    /**
     * 最大距离
     */
    private int mMaxHeight;

    /**
     * 最小移动距离
     */
    private int mMinHeight;

    /**
     * 按下时手指位置,当滑动到极限位置是，此值相应改变，保证Listview的移动作出快速相应
     */
    private float mDownY;

    /**
     * 当前ListView是否已被移动到顶端
     */
    private boolean mIsTop;

    /**
     * 按下时Listview整体移动的位置
     */
    private float mOldTranslationY;

    /**
     * 小ViewTAG，当ListView实际高度比最大高度小时为true
     */
    private boolean mSmallListViewTag = false;

    /**
     * 当前移动状态
     */
    private int mState = LISTVIEW_MOVE;

    /**
     * 上一次记录的手指位置，为判断当前移动趋势
     */
    private float mLastRawY;

    public RetractableListView(Context context) {
        super(context);
        init();
    }

    public RetractableListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public RetractableListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * 初始化显示最大值和显示最小值
     */
    private void init() {
        mMaxHeight = 800;
        mMinHeight = 400;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
        int mListViewHeight = getTotalHeightofListView();
        if (mMaxHeight > mListViewHeight) {
            mMaxHeight = mListViewHeight;
            mSmallListViewTag = true;
        }
        if (mMinHeight > mListViewHeight) {
            mMinHeight = mListViewHeight;
        }
        params.height = mMaxHeight;
        setLayoutParams(params);

        mParentHeight = ((View) getParent()).getHeight();
        mDefaultTranslationY = mParentHeight - mMinHeight;
        setTranslationY(mDefaultTranslationY);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getRawY();
                mLastRawY = ev.getRawY();
                mOldTranslationY = getTranslationY();
                break;
            case MotionEvent.ACTION_MOVE:
                changeState(ev);
                if (mState == LISTVIEW_MOVE) {
                    float a = mOldTranslationY + (ev.getRawY() - mDownY);
                    if (a > mDefaultTranslationY) {
                        mDownY += (ev.getRawY() - mLastRawY);
                        a = mDefaultTranslationY;
                    } else if (a < mParentHeight - mMaxHeight) {
                        mDownY -= (ev.getRawY() - mLastRawY);
                        a = mParentHeight - mMaxHeight;
                    }
                    setTranslationY(a);
                    mLastRawY = ev.getRawY();
                    return true;
                }
                mLastRawY = ev.getRawY();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return super.onTouchEvent(ev);
    }


    /**
     * 改变当前移动模式
     * LISTVIEW_MOVE：listview整体移动
     * ITEM_MOVE：listview中item移动
     */
    private void changeState(MotionEvent ev) {
        if (mLastRawY < ev.getRawY() && (judgeScrollTop() || mSmallListViewTag)) {
            mDownY = ev.getRawY();
            mOldTranslationY = getTranslationY();
            mState = LISTVIEW_MOVE;
        } else if (getTranslationY() == (mParentHeight - mMaxHeight) && mState == LISTVIEW_MOVE && mLastRawY > ev.getRawY()) {
            mState = ITEM_MOVE;
        }
    }

    /**
     * 判断ListView是否已滑动到顶部
     */
    private boolean judgeScrollTop() {
        if (getChildCount() > 0 &&
                getFirstVisiblePosition() == 0 &&
                getChildAt(0).getTop() == getPaddingTop()) {
            if (!mIsTop) {
                mIsTop = true;
                return true;
            }
        } else {
            if (mIsTop) {
                mIsTop = false;
            }
        }
        return false;
    }

    /**
     * 获得ListView的真实高度
     */
    private int getTotalHeightofListView() {
        ListAdapter mAdapter = getAdapter();
        if (mAdapter == null) {
            return 0;
        }
        int totalHeight = 0;
        for (int i = 0; i < mAdapter.getCount(); i++) {
            View mView = mAdapter.getView(i, null, this);
            mView.measure(
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            //mView.measure(0, 0);
            totalHeight += mView.getMeasuredHeight();
        }
        return totalHeight + (getDividerHeight() * (mAdapter.getCount() - 1));
    }

}
